﻿using Algorithm.Check;
using Common;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using static Algorithm.Check.ExtensionForCRC16;

namespace NewDevice.RS485
{
    /// <summary>
    /// LWGY涡轮流量计控制类
    /// 提供基于Modbus协议的涡轮流量计通信功能
    /// </summary>
    public class FlowMeter_LWGY
    {
        /// <summary>
        /// 设备名称
        /// </summary>
        private string deviceName = "LWGY涡轮流量计";

        /// <summary>
        /// 串口通信对象
        /// </summary>
        private readonly SerialBase serial;

        /// <summary>
        /// 构造函数
        /// </summary>
        /// <param name="bus">串口通信对象</param>
        public FlowMeter_LWGY(SerialBase bus)
        {
            this.serial = bus;
        }

        /// <summary>
        /// 读取Modbus寄存器数据
        /// </summary>
        /// <param name="slaveId">从机地址(1-247)</param>
        /// <param name="address">寄存器起始地址</param>
        /// <param name="length">要读取的寄存器数量</param>
        /// <returns>读取到的寄存器数据字节数组</returns>
        /// <exception cref="Exception">当返回数据长度与预期不符时抛出异常</exception>
        public byte[] ReadRegister(int slaveId, int address, int length)
        {
            byte[] cmd = new byte[] {
                (byte)slaveId,
                0x03, // 功能码:读保持寄存器
                (byte)((address&0xff00)>>8), // 寄存器地址高字节
                (byte)((address&0xff)), // 寄存器地址低字节
                (byte)((length&0xff00)>>8), // 读取数量高字节
                (byte)((length&0xff)), // 读取数量低字节
            };
            cmd = cmd.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            byte[] recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);
            if (length * 2 != recv[2])
            {
                throw new Exception($"[{deviceName}] 读寄存器错误，返回数据与长度异常");
            }
            return recv.Skip(3).Take((recv[2])).ToArray();
        }

        /// <summary>
        /// 写入单个寄存器
        /// </summary>
        /// <param name="slaveId">从机地址(1-247)</param>
        /// <param name="address">寄存器地址</param>
        /// <param name="data">要写入的数据(2字节)</param>
        /// <exception cref="Exception">当返回数据与发送数据不匹配时抛出异常</exception>
        private void WriteRegister(int slaveId, int address, byte[] data)
        {
            byte[] cmd = new byte[] {
                (byte)slaveId,
                0x06, // 功能码:写单个寄存器
                (byte)((address&0xff00)>>8), // 寄存器地址高字节
                (byte)((address&0xff)), // 寄存器地址低字节
                data[0], // 写入数据高字节
                data[1], // 写入数据低字节
            };
            cmd = cmd.WithCRC16(CRC16Type.Modbus).ToArray();
            serial.deviceName = deviceName;
            byte[] recv = serial.SendBytesAndWaitResponse(cmd, ReceivedDataCheck.FuncModbusCRC16Check);
            if (!cmd.SequenceEqual(recv))
            {
                throw new Exception($"[{deviceName}] GetDO错误，返回数据与长度异常");
            }
        }

        /// <summary>
        /// 读取实时流量值
        /// </summary>
        /// <param name="slaveId">从机地址</param>
        /// <returns>实时流量值(单位: 根据仪表设定)</returns>
        /// <remarks>
        /// 从寄存器地址0x0102读取2个寄存器(4字节)的浮点数流量值
        /// 需要调整字节顺序为大端模式
        /// </remarks>
        public float GetRealtimeTraffic(int slaveId)
        {
            byte[] byteTraffic = ReadRegister(slaveId, 0x0102, 2);
            // 调整字节顺序为大端模式
            byte[] byteTrafficDeal = new byte[] {
                byteTraffic[1], byteTraffic[0],
                byteTraffic[3], byteTraffic[2],
            };

            float traffic = BitConverter.ToSingle(byteTrafficDeal, 0);
            Mylog.Info(message: $"实时流量为:[{traffic}]", deviceName: deviceName);
            return traffic;
        }

        /// <summary>
        /// 读取累计流量值
        /// </summary>
        /// <param name="slaveId">从机地址</param>
        /// <returns>累计流量值(单位: 根据仪表设定)</returns>
        /// <remarks>
        /// 从寄存器地址0x0100读取2个寄存器(4字节)的浮点数流量值
        /// 需要调整字节顺序为大端模式
        /// </remarks>
        public float GetTotalTraffic(int slaveId)
        {
            byte[] byteTraffic = ReadRegister(slaveId, 0x0100, 2);
            // 调整字节顺序为大端模式
            byte[] byteTrafficDeal = new byte[] {
                byteTraffic[1], byteTraffic[0],
                byteTraffic[3], byteTraffic[2],
            };

            float traffic = BitConverter.ToSingle(byteTrafficDeal, 0);
            Mylog.Info(deviceName: deviceName, message: $" 累计流量为[{traffic}]");
            return traffic;
        }

        /// <summary>
        /// 清空累计流量值
        /// </summary>
        /// <param name="slaveId">从机地址</param>
        /// <remarks>
        /// 向寄存器地址0x0100写入0值来清空累计流量
        /// </remarks>
        public void ClearTotalTraffic(int slaveId)
        {
            WriteRegister(slaveId, 0x0100, new byte[] { 0x00, 0x00 });
        }

        /// <summary>
        /// 检查设备是否在线
        /// </summary>
        /// <param name="slaveId">从机地址，默认为1</param>
        /// <returns>true表示设备在线，false表示设备离线</returns>
        public bool IsDeviceOnline(int slaveId = 1)
        {
            try
            {
                GetRealtimeTraffic(slaveId);
                return true;
            }
            catch (Exception ex)
            {
                Mylog.Error(message: $"{deviceName}不在线,{ex.Message}", ex: ex);
                return false;
            }
        }
    }
}